package net.orange.architecture.db;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;

import net.orange.architecture.system.SystemParameters;
import net.orange.architecture.web.Web;

/**
 * The set of the conditions.
 */
public class Conditions {

	/**
	 * The set of conditions
	 */
	private Collection<ConditionPair> items;

	/**
	 * Default constructor which initialize the conditions set.
	 */
	public Conditions() {
		items = new ArrayList<ConditionPair>();
	}

	/**
	 * Convert the condition map to conditions.
	 * 
	 * @param arg
	 *            The condition map.
	 * @return The conditions
	 * @throws Exception
	 */
	public static Conditions toConditions(Map arg) throws Exception {
		if (arg == null)
			return null;

		Conditions cds = new Conditions();

		for (Object element : arg.keySet()) {
			Object value = arg.get((String) element);
			if (value != null && value instanceof String)
				setParameter(cds, Web.getSimpleParameterName((String) element),
						(String) value);
		}

		return cds;
	}

	/**
	 * Convert the condition string to conditions.
	 * 
	 * @param s
	 *            The condition string.
	 * @return The conditions.
	 */
	public static Conditions toConditions(String s) throws Exception {
		if (s == null || s.length() <= 0)
			return null;

		String[] pairs = s.split(";");
		Conditions cds = new Conditions();

		for (int i = 0; i < pairs.length; i++) {
			String pair = pairs[i];
			if (pair.indexOf("=") >= 0) {
				String name = pair.substring(0, pair.indexOf("="));
				String value = pair.substring(pair.indexOf("=") + 1, pair
						.length());

				if (name != null && value.length() > 0 && value != null
						&& value.length() > 0)
					setParameter(cds, name, value);

			}
		}
		return cds;
	}

	/**
	 * Add the search parameter in list condition.
	 * 
	 * @param lc
	 *            The conditions.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value.
	 */
	protected static void setParameter(Conditions lc, String fn, String sv)
			throws Exception {
		if (fn.startsWith("s") && fn.indexOf("_") > 0)
			setParameterAsString(lc, fn, sv);
		if (fn.startsWith("i") && fn.indexOf("_") > 0)
			setParameterAsInteger(lc, fn, sv);
		if (fn.startsWith("l") && fn.indexOf("_") > 0)
			setParameterAsLong(lc, fn, sv);
		if (fn.startsWith("d") && fn.indexOf("_") > 0)
			setParameterAsDouble(lc, fn, sv);
		if (fn.startsWith("f") && fn.indexOf("_") > 0)
			setParameterAsFloat(lc, fn, sv);
		if (fn.startsWith("t") && fn.indexOf("_") > 0)
			setParameterAsDate(lc, fn, sv);
	}

	/**
	 * Add the search parameter (string type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value.
	 */
	private static void setParameterAsString(Conditions lc, String fn, String sv) {
		// String Parameters.
		if (sv == null || sv.length() <= 0)
			return;

		if (fn.startsWith("sr_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.LIKE), sv);
		}
		if (fn.startsWith("si_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.STR_IN), sv);
		}
		if (fn.startsWith("s_")) {
			setParameter(lc, fn.substring(2, fn.length()), new Operator(
					Operator.EQUAL), sv);
		}
		if (fn.startsWith("sn_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.IS_NULL), sv);
		}
		if (fn.startsWith("snn_")) {
			setParameter(lc, fn.substring(4, fn.length()), new Operator(
					Operator.IS_NOT_NULL), sv);
		}
	}

	/**
	 * Add the search parameter (int type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value.
	 */
	private static void setParameterAsInteger(Conditions lc, String fn,
			String sv) {
		// Integer Parameters.
		if (sv == null || sv.length() <= 0)
			return;

		if (fn.startsWith("i_")) {
			setParameter(lc, fn.substring(2, fn.length()), new Operator(
					Operator.EQUAL), Integer.parseInt(sv));
		}
		if (fn.startsWith("il_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.LARGER_THAN), Integer.parseInt(sv));
		}
		if (fn.startsWith("inl_")) {
			setParameter(lc, fn.substring(4, fn.length()), new Operator(
					Operator.NOT_LARGER_THAN), Integer.parseInt(sv));
		}
		if (fn.startsWith("ie_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.LESS_THAN), Integer.parseInt(sv));
		}
		if (fn.startsWith("ine_")) {
			setParameter(lc, fn.substring(4, fn.length()), new Operator(
					Operator.NOT_LESS_THAN), Integer.parseInt(sv));
		}

	}

	/**
	 * Add the search parameter (long type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value.
	 */
	private static void setParameterAsLong(Conditions lc, String fn, String sv) {
		// Long Parameters.
		if (sv == null || sv.length() <= 0)
			return;

		if (fn.startsWith("l_")) {
			setParameter(lc, fn.substring(2, fn.length()), new Operator(
					Operator.EQUAL), Long.parseLong(sv));
		}
		if (fn.startsWith("ll_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.LARGER_THAN), Long.parseLong(sv));
		}
		if (fn.startsWith("lnl_")) {
			setParameter(lc, fn.substring(4, fn.length()), new Operator(
					Operator.NOT_LARGER_THAN), Long.parseLong(sv));
		}
		if (fn.startsWith("le_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.LESS_THAN), Long.parseLong(sv));
		}
		if (fn.startsWith("lne_")) {
			setParameter(lc, fn.substring(4, fn.length()), new Operator(
					Operator.NOT_LESS_THAN), Long.parseLong(sv));
		}
	}

	/**
	 * Add the search parameter (double type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value.
	 */
	private static void setParameterAsDouble(Conditions lc, String fn, String sv) {
		// Double Parameters.
		if (sv == null || sv.length() <= 0)
			return;

		if (fn.startsWith("d_")) {
			setParameter(lc, fn.substring(2, fn.length()), new Operator(
					Operator.EQUAL), Double.parseDouble(sv));
		}
		if (fn.startsWith("dl_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.LARGER_THAN), Double.parseDouble(sv));
		}
		if (fn.startsWith("dnl_")) {
			setParameter(lc, fn.substring(4, fn.length()), new Operator(
					Operator.NOT_LARGER_THAN), Double.parseDouble(sv));
		}
		if (fn.startsWith("de_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.LESS_THAN), Double.parseDouble(sv));
		}
		if (fn.startsWith("dne_")) {
			setParameter(lc, fn.substring(4, fn.length()), new Operator(
					Operator.NOT_LESS_THAN), Double.parseDouble(sv));
		}
	}

	/**
	 * Add the search parameter (float type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value.
	 */
	private static void setParameterAsFloat(Conditions lc, String fn, String sv) {
		// Float Parameters.
		if (sv == null || sv.length() <= 0)
			return;

		if (fn.startsWith("f_")) {
			setParameter(lc, fn.substring(2, fn.length()), new Operator(
					Operator.EQUAL), Float.parseFloat(sv));
		}
		if (fn.startsWith("fl_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.LARGER_THAN), Float.parseFloat(sv));
		}
		if (fn.startsWith("fnl_")) {
			setParameter(lc, fn.substring(4, fn.length()), new Operator(
					Operator.NOT_LARGER_THAN), Float.parseFloat(sv));
		}
		if (fn.startsWith("fe_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.LESS_THAN), Float.parseFloat(sv));
		}
		if (fn.startsWith("fne_")) {
			setParameter(lc, fn.substring(4, fn.length()), new Operator(
					Operator.NOT_LESS_THAN), Float.parseFloat(sv));
		}
	}

	/**
	 * Add the search parameter (date type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value.
	 */
	private static void setParameterAsDate(Conditions lc, String fn, String sv)
			throws Exception {
		// Date Parameters.
		if (sv == null || sv.length() <= 0)
			return;

		if (fn.startsWith("t_")) {
			setParameter(lc, fn.substring(2, fn.length()), new Operator(
					Operator.EQUAL), parseDate(sv));
		}
		if (fn.startsWith("tl_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.LARGER_THAN), parseDate(sv));
		}
		if (fn.startsWith("tnl_")) {
			setParameter(lc, fn.substring(4, fn.length()), new Operator(
					Operator.NOT_LARGER_THAN), parseDate(sv));
		}
		if (fn.startsWith("te_")) {
			setParameter(lc, fn.substring(3, fn.length()), new Operator(
					Operator.LESS_THAN), parseDate(sv));
		}
		if (fn.startsWith("tne_")) {
			setParameter(lc, fn.substring(4, fn.length()), new Operator(
					Operator.NOT_LESS_THAN), parseDate(sv));
		}
	}

	/**
	 * Add the search parameter (string type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value (string type).
	 */
	private static void setParameter(Conditions lc, String fn, Operator or,
			String dv) {
		if (dv != null && dv.length() > 0)
			lc.add(new Condition(fn, or, dv));
	}

	/**
	 * Add the search parameter (int type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value (int type).
	 */
	private static void setParameter(Conditions lc, String fn, Operator or,
			int dv) {
		lc.add(new Condition(fn, or, dv));
	}

	/**
	 * Add the search parameter (long type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value (long type).
	 */
	private static void setParameter(Conditions lc, String fn, Operator or,
			long dv) {
		lc.add(new Condition(fn, or, dv));
	}

	/**
	 * Add the search parameter (float type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value (float type).
	 */
	private static void setParameter(Conditions lc, String fn, Operator or,
			float dv) {
		lc.add(new Condition(fn, or, dv));
	}

	/**
	 * Add the search parameter (double type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value (float type).
	 */
	private static void setParameter(Conditions lc, String fn, Operator or,
			double dv) {
		lc.add(new Condition(fn, or, dv));
	}

	/**
	 * Add the search parameter (date type) in list condition.
	 * 
	 * @param lc
	 *            The list condition.
	 * @param fn
	 *            The search parameter field name.
	 * @param dv
	 *            The search parameter field value (date type).
	 */
	private static void setParameter(Conditions lc, String fn, Operator or,
			Date dv) {
		if (dv != null)
			lc.add(new Condition(fn, or, dv));
	}

	/**
	 * Add a a condition pair to collection,default the relation is AND.
	 * 
	 * @param cr
	 *            The relation pair.
	 */
	public void add(ConditionPair cr) {
		items.add(cr);
	}

	/**
	 * Add a condition to collection,default the relation is AND.
	 * 
	 * @param cd
	 *            The condition.
	 */
	public void add(Condition cd) {
		items.add(new ConditionPair(cd, new Relation(Relation.AND)));
	}

	/**
	 * Add a condition to collection.
	 * 
	 * @param cd
	 *            The condition.
	 * @param re
	 *            The relation.
	 */
	public void add(Condition cd, Relation re) {
		items.add(new ConditionPair(cd, re));
	}

	/**
	 * Add another conditions in it
	 * 
	 * @param cds
	 *            Another conditions
	 */
	public void add(Conditions cds) {
		if (cds != null)
			items.addAll(cds.items);
	}

	/**
	 * Remove the Condition Pair in collection.
	 * 
	 * @param cr
	 *            The ConditionPair.
	 */
	public void remove(ConditionPair cr) {
		items.remove(cr);
	}

	/**
	 * Clear the collection.
	 */
	public void clear() {
		items.clear();
	}

	/**
	 * The iterator of the condition pair.
	 * 
	 * @return Returns the condition collection.
	 */
	public Iterator<ConditionPair> iterator() {
		return items.iterator();
	}

	/**
	 * The array of the condition array.
	 * 
	 * @return Returns the condition array.
	 */
	public ConditionPair[] toArray() {
		ConditionPair[] array = new ConditionPair[items.size()];
		return (ConditionPair[]) items.toArray(array);
	}

	/**
	 * Return whether the set is empty.
	 * 
	 * @return Returns whether the collection is empty.
	 */
	public boolean isEmpty() {
		return items.isEmpty();
	}

	/**
	 * @return Return the condition collection.
	 */
	public Collection<ConditionPair> getItems() {
		return items;
	}

	/**
	 * @param fields
	 *            The condition collection to set.
	 */
	public void setItems(Collection<ConditionPair> items) {
		this.items = items;
	}

	/**
	 * Parse the string to date.
	 * 
	 * @param arg
	 *            The target string.
	 * @return the date parse form date.
	 */
	private static Date parseDate(String arg) throws Exception {
		SimpleDateFormat format = new SimpleDateFormat(
				SystemParameters.SRV_DATE_FORMAT);

		if (arg != null && arg.length() > 0)
			return format.parse(arg);
		else
			return null;
	}
}